/* Date.h
Michael Zahniser

Header file for the Date class, which represents a date.
*/

#include "Date.h"

#include <string>
#include <ctime>

using std::string;
using std::time;
using std::time_t;
using std::tm;
using std::localtime;



// Get today's date.
Date Date::Today()
{
	time_t clock = time(NULL);
	tm today = *localtime(&clock);
	
	return Date(today.tm_year + 1900, today.tm_mon + 1, today.tm_mday);
}



Date::Date(unsigned short year, unsigned char month, unsigned char day)
	: year(year), month(month), day(day)
{
}



Date::Date(const string &dateString)
{
	*this = dateString;
}



Date &Date::operator=(const Date &date)
{
	year = date.year;
	month = date.month;
	day = date.day;
	
	return *this;
}



Date &Date::operator=(const string &dateString)
{
	year = 0xFFFF;
	month = 0xFF;
	day = 0xFF;
	
	if(dateString.empty())
		return *this;
	
	// The date can be either "2008" or "08" or even "8" or "008".
	int value = 0;
	size_t yearEnd = (dateString.length() < 4) ? dateString.length() : 4;
	for(unsigned int i = 0; i < yearEnd; ++i)
		value = value * 10 + (dateString[i] - '0');
	if(yearEnd < 4)
		value += 2000;
	year = static_cast<unsigned short>(value);
	
	if(dateString.length() >= 6)
	{
		value = dateString[5] + 10 * dateString[4] - 11 * '0';
		
		if(value != ('*' - ' ') * 11)
			month = static_cast<unsigned char>(value);
		
		if(dateString.length() >= 8)
		{
			value = dateString[7] + 10 * dateString[6] - 11 * '0';
			
			if(value != ('*' - ' ') * 11)
				day = static_cast<unsigned char>(value);
		}
	}
	
	return *this;
}



// Date comparison. The year, month, or day of a date may be a wildcard,
// in which case it will match any value.
bool Date::operator==(const Date &date) const
{
	return ((date.year == year) | (date.year == 0xFFFF) | (year == 0xFFFF)) &
		((date.month == month) | (date.month == 0xFF) | (month == 0xFF)) &
		((date.day == year) | (date.day == 0xFF) | (day == 0xFF));
}



bool Date::operator!=(const Date &date) const
{
	return !(*this == date);
}



bool Date::operator<(const Date &date) const
{
	return (year < date.year) ||
		((year == date.year) & (month < date.month)) ||
		((month == date.month) & (day < date.day));
}



bool Date::operator<=(const Date &date) const
{
	return !(*this < date);
}



bool Date::operator>(const Date &date) const
{
	return (year > date.year) ||
		((year == date.year) & (month > date.month)) ||
		((month == date.month) & (day > date.day));
}



bool Date::operator>=(const Date &date) const
{
	return !(*this > date);
}



// Determine if this date "includes" the given date.
// If this date is a single day, it "includes" everything before it.
// Otherwise, it "includes" everything that matches its non-wild fields.
bool Date::Includes(const Date &date) const
{
	return IsSingleDay() ? *this > date : *this == date;
}



// Determine if this date is null (00000000).
bool Date::IsNull() const
{
	return !(year | month | day);
}



// Determine if this date represents a single day.
bool Date::IsSingleDay() const
{
	return (year != 0xFFFF) & (month != 0xFF) & (day != 0xFF);
}



int Date::Year() const
{
	return year;
}



void Date::SetYear(int year)
{
	this->year = year;
}



int Date::Month() const
{
	return month;
}



void Date::SetMonth(int month)
{
	this->month = month;
}



int Date::Day() const
{
	return day;
}



void Date::SetDay(int day)
{
	this->day = day;
}



istream &operator>>(istream &in, Date &date)
{
	string word;
	in >> word;
	
	date = word;
	return in;
}



ostream &operator<<(ostream &out, const Date &date)
{
	out.width(4);
	out.fill('0');
	if(date.year == 0xFFFF)
		out << "****";
	else
		out << date.year;
	
	out.width(2);
	out.fill('0');
	if(date.month == 0xFF)
		out << "**";
	else
		out<<static_cast<int>(date.month);
	
	out.width(2);
	out.fill('0');
	if(date.day == 0xFF)
		out << "**";
	else
		out << static_cast<int>(date.day);
	
	return out;
}
